﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using LukasKopenec.LocalizationSupport;
using Notoric.Commanding;
using Notoric.Commanding.CommandBase;

namespace Notoric.ViewModel.Elements.CWNotation
{
    /// <summary>
    /// Adapts a <see cref="Model.Elements.CWNotation.KeySignature"/> instance to a Windows Presentation Foundation view.
    /// </summary>
    public class KeySignature : CoreSymbol
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="KeySignature"/> instance.
        /// </summary>
        /// <param name="owner">
        /// <para>
        /// Type: <see cref="Staff"/>
        /// </para>
        /// <para>
        /// The element that directly owns this element.
        /// </para>
        /// </param>
        /// <param name="adaptedElement">
        /// <para>
        /// Type: <see cref="Model.Elements.CWNotation.KeySignature"/>
        /// </para>
        /// <para>
        /// The adapted model element instance.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="adaptedElement"/> is <see langword="null"/>.
        /// </exception>
        /// <exception cref="NullReferenceException">
        /// <paramref name="owner"/> is <see langword="null"/>.
        /// The values of the <see cref="ViewModelElement.UndoStack"/> and <see cref="ViewModelElement.RedoStack"/> properties
        /// are passed directly to the base class (<see cref="ViewModelElement"/>) constructor. Thus, there is not
        /// an occasion to react with a more appropriate exception.
        /// </exception>
        public KeySignature(Staff owner, Model.Elements.CWNotation.KeySignature adaptedElement)
            : base(owner, adaptedElement)
        {
            keyCollection = new List<KeySignatureSign>();
            key = new ReadOnlyCollection<KeySignatureSign>(keyCollection);

            for (byte i = 1; i <= 7; ++i)
                keyCollection.Add(new KeySignatureSign(this, adaptedElement[i]));
        }

        #endregion

        #region ViewModelElement Members

        /// <inheritdoc/>
        public override string ElementNameKey
        {
            get { return elementNameKey; }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Creates an undoable command for changing the key represented by this key signature.
        /// </summary>
        /// <param name="newKey">
        /// <para>
        /// Type: <see cref="Array"/> of <see cref="AccidentalType"/>
        /// </para>
        /// <para>
        /// The new key (collection of 7 accidental types for notes C to B).
        /// </para>
        /// </param>
        /// <returns>
        /// <para>
        /// Type: <see cref="UndoableCommandBase"/>
        /// </para>
        /// <para>
        /// The created command ready for execution.
        /// </para>
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="newKey"/> is <see langword="null"/>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="newKey"/> does not contain exactly 7 elements.
        /// </exception>
        public UndoableCommandBase CreateChangeKeyCommand(AccidentalType[] newKey)
        {
            if (newKey == null)
                throw new ArgumentNullException("newKey");
            if (newKey.Length != 7)
                throw new ArgumentException("Invalid key definition (wrong number of elements)", "newKey");

            var macro = new UndoableMacroCommand(UndoStack, RedoStack);
            macro.Name = String.Format(
                    (string)LocalizationManager.GetValue("Commands.Names.CommitViewModelChanges"),
                    (string)LocalizationManager.GetValue(ElementNameKey)
                    );
            
            for (int i = 0; i < 7; ++i)
            {
                if (key[i].AccidentalType != newKey[i])
                    macro.Commands.Add(
                        new SetterCommand<Model.Elements.CWNotation.AccidentalType>(
                            key[i], 
                            "AccidentalType", 
                            (Model.Elements.CWNotation.AccidentalType)Enum.Parse(typeof(Model.Elements.CWNotation.AccidentalType), newKey[i].ToString()), 
                            UndoStack, 
                            RedoStack
                            )
                        );
            }

            return macro;
        }

        /// <summary>
        /// Changes the key represented by this key signature.
        /// </summary>
        /// <param name="newKey">
        /// <para>
        /// Type: <see cref="Array"/> of <see cref="AccidentalType"/>
        /// </para>
        /// <para>
        /// The new key (collection of 7 accidental types for notes C to B).
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="newKey"/> is <see langword="null"/>
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="newKey"/> does not contain exactly 7 elements.
        /// </exception>
        public void ChangeKey(AccidentalType[] newKey)
        {
            var cmd = CreateChangeKeyCommand(newKey);
            cmd.Execute(null);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the key.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="ReadOnlyCollection{T}"/> of <see cref="KeySignatureSign"/>
        /// </para>
        /// <para>
        /// The key represented by this key signature (a collection of 7 key signature signs).
        /// </para>
        /// </value>
        public ReadOnlyCollection<KeySignatureSign> Key
        {
            get { return key; }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds a user-friendly name of this element.
        /// </summary>
        private const string elementNameKey = "ViewModel.ElementNames.KeySignature";
        /// <summary>
        /// Holds the value of the <see cref="Key"/> property.
        /// </summary>
        private ReadOnlyCollection<KeySignatureSign> key;
        /// <summary>
        /// Holds the key collection.
        /// </summary>
        private List<KeySignatureSign> keyCollection;
        
        #endregion
    }
}
